Popular Searches
Popular Course Categories
Popular Courses

Storing Preferences Using UserDefaults and AppStorage

Storing Preferences Using UserDefaults and AppStorage

iOS Data Storage and Local Databases

Storing Preferences Using UserDefaults and AppStorage

Course: iOS App Development

Topic: Storing Preferences Using UserDefaults and AppStorage

Course Link: iOS Training at JustAcademy

Demo Link: Register for a Course Demo

Website: JustAcademy


1. Introduction to User Preferences in iOS

User preferences are small pieces of information that an application remembers to provide a personalized and consistent experience. For example, an app may remember whether the user prefers dark mode, which language they selected, whether onboarding has been completed, or which display option they last used.

In iOS development, UserDefaults provides a simple key-value storage mechanism for preferences. SwiftUI also provides @AppStorage, a property wrapper that connects a preference stored in UserDefaults to a SwiftUI view.

Why Store Preferences?

  • Personalization: Remember the user's selected theme, language, or layout.
  • Persistence: Keep preference values available after the app closes and opens again.
  • Convenience: Avoid asking users to select the same options repeatedly.
  • Improved experience: Restore appropriate interface settings when the app launches.
  • Simple configuration: Store lightweight settings without creating a full database.

2. What Is UserDefaults?

UserDefaults is an Apple Foundation API used to save small amounts of configuration data as key-value pairs. Each value is associated with a unique key, which the application uses to retrieve or update it.

For example, a setting might use the key preferredLanguage and the value English. The application can read that value the next time it needs to configure the interface.

Common Use Cases

  • Saving a dark mode preference.
  • Remembering whether a welcome screen has been shown.
  • Storing a selected language or measurement unit.
  • Saving a compact or expanded layout preference.
  • Remembering a non-sensitive application configuration option.

Types of Values

UserDefaults supports common property-list-compatible values, including strings, numbers, Boolean values, dates, data, arrays, and dictionaries containing compatible values.

Data Type Example Typical Use
String "English" Preferred language
Bool true Dark mode enabled
Int 3 Selected display option
Double 1.25 Playback speed preference
Date Date() Simple date-related setting
Data Data value Small binary preference data when appropriate

UserDefaults is intended for lightweight settings, not large collections, complex relational records, or sensitive credentials.

3. Accessing UserDefaults in Swift

The most common way to access the standard preferences store is through UserDefaults.standard.

import Foundation

let defaults = UserDefaults.standard

This shared instance provides methods to save and retrieve supported values. Choose clear, consistent keys so that the purpose of each setting is easy to understand.

4. Saving Preferences Using UserDefaults

Use the set(_:forKey:) method to store a value under a key. If a value already exists for that key, setting a new value replaces the previous value.

Example: Saving a Boolean Value

import Foundation

let defaults = UserDefaults.standard

defaults.set(true, forKey: "hasCompletedOnboarding")

This saves a Boolean value indicating that onboarding has been completed.

Example: Saving a String

let defaults = UserDefaults.standard

defaults.set("English", forKey: "preferredLanguage")

Example: Saving Multiple Preferences

let defaults = UserDefaults.standard

defaults.set(true, forKey: "notificationsEnabled")
defaults.set("Dark", forKey: "appearanceMode")
defaults.set(2, forKey: "selectedTabIndex")
defaults.set(1.5, forKey: "playbackSpeed")

Each value is stored independently using its key. Keep key names consistent across the application to avoid accidentally creating duplicate settings.

5. Reading Values from UserDefaults

UserDefaults provides type-specific methods for reading stored values. If a setting has not been saved, many of these methods return a default value, such as false for a Boolean or 0 for a number. String and object accessors can return optional values.

Reading a Boolean

let defaults = UserDefaults.standard

let onboardingCompleted =
    defaults.bool(forKey: "hasCompletedOnboarding")

print(onboardingCompleted)

Reading a String with a Fallback

let defaults = UserDefaults.standard

let language = defaults.string(forKey: "preferredLanguage")
    ?? "English"

print("Selected language: \(language)")

Reading an Integer

let selectedTab = UserDefaults.standard
    .integer(forKey: "selectedTabIndex")

print("Selected tab: \(selectedTab)")

Important: A missing Boolean value and a saved false value both produce false when read with bool(forKey:). If the app needs to distinguish between these states, use object(forKey:) to check whether a value exists.

let defaults = UserDefaults.standard

if defaults.object(forKey: "notificationsEnabled") != nil {
    let isEnabled = defaults.bool(forKey: "notificationsEnabled")
    print("Saved preference: \(isEnabled)")
} else {
    print("No preference has been saved yet.")
}

6. Updating and Removing Preferences

Preferences can be updated by saving a new value under the same key. A preference can also be removed when the application no longer needs it.

Updating an Existing Value

let defaults = UserDefaults.standard

defaults.set("Hindi", forKey: "preferredLanguage")

let updatedLanguage = defaults.string(
    forKey: "preferredLanguage"
) ?? "English"

print(updatedLanguage)

Removing a Preference

let defaults = UserDefaults.standard

defaults.removeObject(forKey: "preferredLanguage")

Removing a key deletes the app's saved value for that key. A registered fallback value may still be returned if one exists.

Checking Whether a Preference Exists

let defaults = UserDefaults.standard

if defaults.object(forKey: "preferredLanguage") != nil {
    print("A language preference is available.")
} else {
    print("No saved language preference was found.")
}

7. Registering Default Preference Values

Applications often need sensible initial settings before a user changes anything. The register(defaults:) method registers fallback values for keys that do not already have a value in a higher-priority defaults domain.

import Foundation

let defaults = UserDefaults.standard

defaults.register(defaults: [
    "notificationsEnabled": true,
    "preferredLanguage": "English",
    "fontScale": 1.0
])

let notificationsEnabled =
    defaults.bool(forKey: "notificationsEnabled")

let language =
    defaults.string(forKey: "preferredLanguage") ?? "English"

print(notificationsEnabled)
print(language)

Registered values are fallbacks rather than permanent user selections. Register them early in the application's launch process, and use a consistent set of keys throughout the project.

8. Introduction to AppStorage

@AppStorage is a SwiftUI property wrapper that reads and writes values through UserDefaults and updates a view when the corresponding preference changes. It reduces the amount of manual code required to connect a setting to the interface.

With ordinary UserDefaults code, a developer may need to read a value, update it, and separately ensure that the user interface reflects the change. AppStorage connects the stored value to SwiftUI's view-update mechanism.

Basic AppStorage Syntax

import SwiftUI

struct SettingsView: View {
    @AppStorage("notificationsEnabled")
    private var notificationsEnabled = true

    var body: some View {
        Toggle(
            "Enable Notifications",
            isOn: $notificationsEnabled
        )
    }
}

In this example, the property uses the key notificationsEnabled. The initial value provides a default when no saved value exists. The projected value, accessed using $notificationsEnabled, provides a binding suitable for controls such as Toggle.

Important Features of AppStorage

  • Connects SwiftUI properties to UserDefaults.
  • Reads existing preference values using the specified key.
  • Writes changes back to the defaults store.
  • Updates dependent SwiftUI views when the stored value changes.
  • Supports common preference types, including Boolean values, strings, and numbers.
  • Reduces the need for manual synchronization between a preference and a view.

9. Storing a Boolean Preference with AppStorage

Boolean settings are commonly used for toggles such as dark mode, notifications, sound, and automatic playback.

import SwiftUI

struct NotificationSettingsView: View {
    @AppStorage("notificationsEnabled")
    private var notificationsEnabled = true

    var body: some View {
        Form {
            Toggle(
                "Enable Notifications",
                isOn: $notificationsEnabled
            )

            Text(
                notificationsEnabled
                ? "Notifications are enabled."
                : "Notifications are disabled."
            )
        }
        .navigationTitle("Notifications")
    }
}

When the user changes the toggle, SwiftUI updates the property and the preference is stored through UserDefaults. The explanatory text also refreshes to reflect the new state.

10. Storing Strings and Numbers with AppStorage

AppStorage can store simple preferences other than Boolean values. These may include a language, a display name, a preferred unit, or a numeric setting.

Saving a Preferred Language

import SwiftUI

struct LanguageSettingsView: View {
    @AppStorage("preferredLanguage")
    private var preferredLanguage = "English"

    var body: some View {
        Form {
            Picker("Language", selection: $preferredLanguage) {
                Text("English").tag("English")
                Text("Hindi").tag("Hindi")
                Text("French").tag("French")
            }

            Text("Selected: \(preferredLanguage)")
        }
        .navigationTitle("Language")
    }
}

Saving a Numeric Preference

import SwiftUI

struct PlaybackSettingsView: View {
    @AppStorage("playbackSpeed")
    private var playbackSpeed = 1.0

    var body: some View {
        Form {
            Slider(
                value: $playbackSpeed,
                in: 0.5...2.0,
                step: 0.25
            )

            Text(
                "Playback speed: \(playbackSpeed, specifier: "%.2f")x"
            )
        }
    }
}

Use appropriate value types and validate values when settings must remain within a particular range.

11. Building a Dark Mode Preference Screen

A dark mode preference is a useful example of persistent settings. The following screen stores the user's choice and uses it to configure the appearance of the interface.

import SwiftUI

struct AppearanceSettingsView: View {
    @AppStorage("isDarkMode")
    private var isDarkMode = false

    var body: some View {
        Form {
            Section("Appearance") {
                Toggle("Use Dark Mode", isOn: $isDarkMode)

                Text(
                    isDarkMode
                    ? "Dark appearance selected"
                    : "Light appearance selected"
                )
            }
        }
        .navigationTitle("Appearance")
        .preferredColorScheme(isDarkMode ? .dark : .light)
    }
}

The preferredColorScheme modifier applies the chosen appearance to the relevant view hierarchy. In a larger application, decide whether the setting should control the whole app or whether the app should follow the device's system appearance by default.

12. Complete Example: A User Preferences Screen

This example combines multiple preference types into one SwiftUI settings screen. It stores a display name, notification preference, theme preference, and language selection.

import SwiftUI

struct UserPreferencesView: View {
    @AppStorage("displayName")
    private var displayName = ""

    @AppStorage("notificationsEnabled")
    private var notificationsEnabled = true

    @AppStorage("isDarkMode")
    private var isDarkMode = false

    @AppStorage("preferredLanguage")
    private var preferredLanguage = "English"

    var body: some View {
        NavigationStack {
            Form {
                Section("Profile") {
                    TextField("Display name", text: $displayName)
                        .textInputAutocapitalization(.words)
                }

                Section("Notifications") {
                    Toggle(
                        "Enable Notifications",
                        isOn: $notificationsEnabled
                    )
                }

                Section("Appearance") {
                    Toggle("Dark Mode", isOn: $isDarkMode)
                }

                Section("Language") {
                    Picker(
                        "Preferred Language",
                        selection: $preferredLanguage
                    ) {
                        Text("English").tag("English")
                        Text("Hindi").tag("Hindi")
                        Text("French").tag("French")
                    }
                }

                Section("Summary") {
                    Text(
                        "Welcome, \(displayName.isEmpty ? "Guest" : displayName)"
                    )

                    Text(
                        "Notifications: \(notificationsEnabled ? "On" : "Off")"
                    )

                    Text(
                        "Language: \(preferredLanguage)"
                    )
                }
            }
            .navigationTitle("Preferences")
            .preferredColorScheme(isDarkMode ? .dark : .light)
        }
    }
}

How the Example Works

  1. Property declarations: Each AppStorage property is associated with a unique preference key.
  2. Initial values: Default values are supplied for settings that have not been customized.
  3. Two-way binding: Controls use the $ prefix to read and update the associated property.
  4. Persistence: Updated values are written through UserDefaults.
  5. Interface updates: SwiftUI refreshes dependent views when preferences change.
  6. Restoration: Saved preferences are read again when the view is recreated or the app launches later.

13. Difference Between UserDefaults and AppStorage

Feature UserDefaults AppStorage
API type Foundation class SwiftUI property wrapper
Main purpose Directly read and write preference values Connect preference values to SwiftUI views
Typical access UserDefaults.standard @AppStorage("key")
Interface updates Must be integrated with the UI update approach being used Automatically invalidates dependent SwiftUI views when the stored value changes
Usable outside SwiftUI Yes Designed for SwiftUI view properties
Direct key-value access Yes Accessed through the wrapped property
Best suited for General preference management and non-SwiftUI code SwiftUI controls and settings screens

Key point: AppStorage is built on UserDefaults; it is not a separate database. Use UserDefaults for direct programmatic access and AppStorage when a preference should be closely connected to a SwiftUI view.

14. Organizing Preference Keys

Using unrelated string literals throughout an application can lead to spelling mistakes and inconsistent keys. A small namespace can help organize preference names.

enum PreferenceKeys {
    static let isDarkMode = "isDarkMode"
    static let notificationsEnabled = "notificationsEnabled"
    static let preferredLanguage = "preferredLanguage"
    static let hasCompletedOnboarding = "hasCompletedOnboarding"
}

let defaults = UserDefaults.standard

defaults.set(true, forKey: PreferenceKeys.isDarkMode)

let isDarkMode = defaults.bool(
    forKey: PreferenceKeys.isDarkMode
)

For larger applications, centralize preference definitions when it improves maintainability. Avoid changing a key casually because existing installations may already contain a value under the old key.

15. Resetting Preferences

Applications may offer a reset option so that users can restore settings to their defaults. A reset should affect only the intended preference keys.

Reset Selected Preferences

func resetAppearancePreferences() {
    let defaults = UserDefaults.standard

    defaults.removeObject(forKey: "isDarkMode")
    defaults.removeObject(forKey: "preferredLanguage")
}

After removal, properties using registered or declared defaults can return to their fallback values. If using AppStorage, verify that the relevant views reflect the restored settings.

Important Reset Considerations

  • Do not remove unrelated settings that the user expects to retain.
  • Explain what will be reset before performing a destructive action.
  • Test reset behavior with both previously saved and never-saved preferences.
  • Do not use broad persistent-store deletion as a shortcut for resetting a few settings.

16. Using a Custom UserDefaults Store

By default, AppStorage uses the standard UserDefaults store. SwiftUI can also be configured to use a different store, such as an app-group suite when the application has the required entitlement.

Creating a Suite

import Foundation

let sharedDefaults = UserDefaults(
    suiteName: "group.com.example.myapp"
)

The suite name must match a properly configured App Group identifier when sharing settings with an app extension or another component in the same app group.

Using the Custom Store in SwiftUI

import SwiftUI

struct SharedPreferencesView: View {
    @AppStorage("sharedDisplayMode")
    private var displayMode = "Standard"

    var body: some View {
        Form {
            Picker("Display Mode", selection: $displayMode) {
                Text("Standard").tag("Standard")
                Text("Compact").tag("Compact")
            }
        }
        .defaultAppStorage(
            UserDefaults(suiteName: "group.com.example.myapp")
                ?? .standard
        )
    }
}

This example falls back to the standard store if suite creation fails. In production, configure and validate the intended app-group store explicitly rather than relying on a fallback that might hide a configuration problem.

17. Limitations and Security Considerations

UserDefaults is designed for nonsensitive preferences. Its persistent data is not encrypted by the defaults system, so it should not be treated as a secure storage mechanism.

What Should Not Be Stored in UserDefaults?

  • Passwords and authentication secrets.
  • Private keys or sensitive access tokens.
  • Large collections of application records.
  • Complex relational data requiring advanced queries.
  • Information that must be securely shared or synchronized across devices without a separate design.

Recommended Alternatives

Requirement Suitable Option
Small, nonsensitive preferences UserDefaults or AppStorage
Passwords and sensitive credentials Keychain
Documents and downloaded files File system
Structured records and relationships SwiftData or Core Data
Relational SQL queries SQLite
Cross-device preference synchronization An appropriate cloud or synchronization mechanism

For additional guidance, refer to Apple's UserDefaults documentation and Apple's AppStorage documentation.

18. Common Mistakes and Troubleshooting

Problem Possible Cause Solution
Preference does not appear to save Different keys are used when writing and reading Check that both operations use exactly the same key.
Boolean always appears false No value exists, or the saved value is false Check key existence with object(forKey:) when necessary.
AppStorage does not reflect an expected value Wrong key or different defaults store Verify the key and store configuration.
Setting resets after reinstalling the app App data was removed during reinstallation or testing Do not assume preferences survive app removal.
Two settings conflict Duplicate keys are used for different purposes Use unique, descriptive key names.
Private data is exposed Sensitive values were saved in defaults Move credentials to Keychain and review existing data handling.
Shared settings are unavailable App Group or suite is not configured correctly Check the suite identifier and required entitlement.

19. Testing UserDefaults and AppStorage

Preference functionality should be tested under different conditions, including first launch, normal use, app relaunch, and reset operations.

Testing Checklist

  • Verify default values on a fresh installation.
  • Change a setting and confirm that the interface updates.
  • Close and reopen the app to check persistence.
  • Test missing keys and unexpected values.
  • Confirm that reset actions restore the intended defaults.
  • Check that independent settings use different keys.
  • Verify that sensitive information is not stored in defaults.
  • Test custom stores and app-group settings if the app uses them.

Example: Testing a Saved Preference

import XCTest

final class PreferenceTests: XCTestCase {
    func testLanguagePreference() {
        let defaults = UserDefaults.standard
        let key = "testPreferredLanguage"

        defaults.set("Hindi", forKey: key)

        XCTAssertEqual(
            defaults.string(forKey: key),
            "Hindi"
        )

        defaults.removeObject(forKey: key)
    }
}

This is a simple example using the standard store. In a larger test suite, consider injecting a dedicated test store or using an isolated suite to prevent tests from affecting real application preferences.

20. Practical Project: Personalized Settings Screen

Project Objective: Build a SwiftUI settings screen that saves user preferences and restores them automatically when the application is reopened.

Project Requirements

  • Add a toggle to enable or disable a preference such as notifications.
  • Add a dark mode toggle.
  • Add a language picker.
  • Add a text field for a display name.
  • Persist the values using AppStorage.
  • Display a summary of the selected preferences.
  • Provide a reset option for selected settings.
  • Test that the values remain available after relaunching the app.

Suggested Development Steps

  1. Create a new SwiftUI project in Xcode.
  2. Define AppStorage properties with unique keys and appropriate initial values.
  3. Build the interface using Form, Toggle, Picker, and TextField.
  4. Use property bindings to connect controls to the stored values.
  5. Apply the selected appearance where appropriate.
  6. Implement a reset action for the settings being managed.
  7. Run the app and verify that preferences persist between launches.
  8. Test missing values and confirm that default behavior is correct.

21. Practice Exercises

  1. Save and retrieve a preferred language using UserDefaults.
  2. Create a SwiftUI toggle that remembers the user's dark mode preference.
  3. Use AppStorage to remember whether onboarding has been completed.
  4. Store a playback speed using a Slider and AppStorage.
  5. Build a settings screen with three independent preference keys.
  6. Write a function that removes a specific saved preference.
  7. Register default values for notification and display settings.
  8. Organize preference keys in an enum or another central definition.
  9. Write a unit test that saves, reads, and removes a test preference.
  10. Explain why credentials should be stored in Keychain instead of UserDefaults.

22. Interview Questions and Answers

Q1. What is UserDefaults in iOS?

UserDefaults is a Foundation API for storing and retrieving small, nonsensitive configuration values as key-value pairs.

Q2. What is AppStorage?

AppStorage is a SwiftUI property wrapper that connects a property to UserDefaults and updates dependent views when the preference changes.

Q3. What is the difference between UserDefaults and AppStorage?

UserDefaults provides direct programmatic access to preference values. AppStorage provides a convenient way to connect those values to SwiftUI views.

Q4. Does UserDefaults store values permanently?

Values are persisted across ordinary app launches, but they should not be assumed to survive app deletion, device resets, or other data-removal events.

Q5. Can UserDefaults store custom Swift objects?

It supports property-list-compatible values. Other types generally need to be encoded into a supported representation, such as Data, before storage. Simple preference types are usually preferable.

Q6. Why should passwords not be stored in UserDefaults?

UserDefaults is not designed for sensitive credentials, and its persistent data is not encrypted by the defaults system. Keychain is the appropriate standard option for secrets.

Q7. What does the dollar sign mean in $isDarkMode?

For a property wrapper such as AppStorage, the dollar-prefixed form accesses the projected value, which provides a binding that controls such as Toggle can use to update the property.

Q8. How can you remove a saved preference?

Use UserDefaults.standard.removeObject(forKey:) with the relevant key.

Q9. What is the purpose of register(defaults:)?

It registers fallback values that are used when no higher-priority defaults domain contains a value for the corresponding key.

Q10. Does AppStorage use a separate storage system?

No. AppStorage uses UserDefaults and integrates it with SwiftUI's view-update mechanism.

Q11. Can UserDefaults synchronize settings across multiple devices automatically?

Standard UserDefaults is generally device-local. Cross-device synchronization requires an appropriate cloud or synchronization solution.

Q12. How should preference keys be managed in a large application?

Use clear, unique, consistent key names and consider centralizing them to reduce spelling mistakes and accidental duplication.

23. Quick Revision Checklist

  • Understand key-value storage and the purpose of UserDefaults.
  • Know how to save, read, update, and remove preferences.
  • Understand fallback values and the behavior of missing keys.
  • Use AppStorage to connect preferences to SwiftUI controls.
  • Use unique keys for independent settings.
  • Understand how to register default preference values.
  • Know the difference between UserDefaults and AppStorage.
  • Never treat UserDefaults as secure credential storage.
  • Test persistence, reset behavior, and first-launch defaults.
  • Choose a database or file system for data that exceeds the needs of simple preferences.

24. Conclusion

Storing preferences with UserDefaults and AppStorage is a fundamental skill in iOS development. UserDefaults provides direct access to lightweight settings, while AppStorage makes it easy to connect these settings to SwiftUI controls and keep the interface in sync with saved values.

By using meaningful keys, sensible default values, appropriate storage types, and thorough testing, developers can create applications that remember user choices and provide a smoother experience. Always use storage designed for the data involved, especially when handling sensitive credentials or complex records.

Learn more through the iOS Training course at JustAcademy, explore the JustAcademy website, or register for a course demo.

Previous Next
whatsapp